package search;

import java.io.IOException;
import java.util.LinkedList;

import net.barashev.seclub.io.BufferManager;
import net.barashev.seclub.io.DataMarshaller;
import net.barashev.seclub.io.IteratingSlot;
import net.barashev.seclub.io.OutputSlot;
import net.barashev.seclub.io.file.FileManager;
import net.barashev.seclub.io.file.VFile;
import net.barashev.seclub.io.marshaller.IntegerMarshaller;

public class DiscBufferedIntegerList {
	private int maxMemorySize;
	private boolean readable;
	private boolean inMemory;
	private boolean occurencesOnDisk;
	private FileManager fileMgr;
	private BufferManager bufferMgr;
	private String name;
	private IteratingSlot<?, ? extends DataMarshaller<?>> inputSlot;
	private int idx;
	private OutputSlot<Integer, ? extends DataMarshaller<Integer>> outputSlot;
	private static final int bufferSize = 1024;
	private LinkedList<Integer> list = new LinkedList<Integer>();
	private VFile<Integer> file;

	public DiscBufferedIntegerList(FileManager fileMgr,
			BufferManager bufferMgr, String name, boolean free,
			int maxMemorySize) throws IOException {
		this.fileMgr = fileMgr;
		this.bufferMgr = bufferMgr;
		this.name = name;
		this.maxMemorySize = maxMemorySize;
		idx = 0;
		inMemory = free;
		readable = !free;
		occurencesOnDisk=!free;
		if (readable) {
			this.file=fileMgr.getFile(name);
			inputSlot = bufferMgr.allocateIteratingSlot(bufferSize, file);
			inputSlot.pull();
		}
	}

	public String getName() {
		return name;
	}

	public FileManager getFileMgr() {
		return fileMgr;
	}

	public BufferManager getBufferMgr() {
		return bufferMgr;
	}
	
	public boolean hasNext(){
		if(inMemory){
			return !list.isEmpty();
		}else return (idx<inputSlot.getBlockCount());
	}
	
	public void releaseAll(){
		if(inputSlot!=null){
			inputSlot.release();
		}
		if(outputSlot!=null){
			outputSlot.release();
		}
	}

	public void makeReadable() throws IOException {
		if (!inMemory&!readable) {
			while (list.size() > 0) {
				outputSlot.append(list.removeFirst());
			}
			outputSlot.flush();
			outputSlot.release();
			inputSlot = bufferMgr.allocateIteratingSlot(bufferSize, file);
			inputSlot.pull();
		}
		readable = true;
		idx = 0;
	}

	public int readNext() throws IOException {
		if (list.size() == 0) {
			if (occurencesOnDisk) {
				while (list.size() < maxMemorySize
						& idx < inputSlot.getBlockCount()) {
					list.addLast((Integer) inputSlot.getBlock(idx).get(0));
					idx++;
					if (idx == bufferSize) {
						idx = 0;
						inputSlot.pull();
					}
					idx = idx + (Integer) inputSlot.getBlock(idx).get(0) + 1;
					while (idx > bufferSize - 1) {
						inputSlot.pull();
						idx = idx - bufferSize;
					}
				}
				if (idx == inputSlot.getBlockCount()) {
					inputSlot.release();
					inMemory=true;
				}
			} else {
				while (list.size() < maxMemorySize
						& idx < inputSlot.getBlockCount()) {
					list.addLast((Integer) inputSlot.getBlock(idx).get(0));
					idx++;
					if (idx == bufferSize) {
						idx = 0;
						inputSlot.pull();
					}
				}
				if (idx == inputSlot.getBlockCount()) {
					inputSlot.release();
					inMemory=true;
				}
			}
		}
		return list.removeFirst();
	}

	public void writeNext(int toWrite) throws IOException {
		if (list.size() == maxMemorySize) {
			if (inMemory) {
				if(fileMgr.getFile(name)==null){
					file=fileMgr.createFile(name, new IntegerMarshaller());
				}else{
					file=fileMgr.getFile(name);
				}
				outputSlot = bufferMgr.allocateOutputSlot(file);
				inMemory = false;
			}
			while (list.size() > 0) {
				outputSlot.append(list.removeFirst());
			}
		}
		list.addLast(toWrite);
	}
}
